· 7 min read
Every site made with Starter now swaps pages in place: links are fetched on intent and the main content changes without a reload. Here is how it works, and what it asked of sections.
Owen Park
Engineering
A marketing site is small. Most have fewer than thirty pages, and most visitors look at three of them. That makes it a good candidate for a trick that would be reckless on a large app: fetch the next page before the visitor asks for it, and swap it in without a full load.
Since 2.13, every Starter site does this out of the box. Hovering a link for 80 milliseconds, focusing it, or touching it fetches the page into memory. The click replaces the page’s <main> with the new one, keeps the header and footer where they are, and updates the title, the history and the scroll position. On our own site, the median click-to-paint went from 410 ms to 38 ms.
We did not build a client-side router, and we did not turn sites into single-page apps. Every page is still a complete HTML document that the server renders. If the script is blocked, the network is slow, or the link goes somewhere unusual, the browser loads the page the ordinary way. The floor is a normal website.
The runtime is one small file. It ignores other origins, downloads, mailto: and tel: links, anything with target="_blank", and clicks with a modifier key. A link can opt out with a single attribute, which we use for the sign-in link into the app.
The hard part was not the swap. It was every script that assumed it would only run once.
A pricing toggle that binds its click handler on page load works on the page you land on and nowhere else. A scroll reveal that sets up its observer on load leaves every later page’s content invisible. We found eleven of these across the section library the first afternoon we turned it on.
The fix was a convention. Anything that touches page content is a function that takes a root element and is called twice: once on load, and again whenever a new page arrives.
function setUp(root) {
reveals(root);
pricingToggle(root);
}
setUp(document);
document.addEventListener('instant:navigated', (e) => setUp(e.detail.main));
Sections that carry their own small script write it as a self-contained function that checks whether it has already run on its element. The runtime re-creates those scripts when their page arrives, so they keep working.
Fetching ahead only pays if pages are light enough to be worth fetching. So the rest of the usual advice still applies: images below the fold load lazily, every image has a width and a height so nothing jumps after a swap, and the hero image is the only one fetched eagerly.
We also cap what we fetch. The navigation’s links are fetched once the page is idle; everything else waits for intent. On a phone with data saver on, nothing is fetched ahead at all.
If your site was installed before 2.13, run the update and the switch is already on in the layout. Click between two pages with the network panel open and you will see one request on hover and nothing on click. That nothing is the whole feature.
What shipped, what changed, and one marketing page worth stealing from another team. That is all.
No spam. Unsubscribe any time.